/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package steg.steki.png;

import steg.steki.StegException;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.InflaterInputStream;

/**
 * Klasa przechowująca PNGChunk, czyli dane o pliku PNG.
 *
 * @author Tryllion
 */
class PNGDane {

    private int ileChunks;
    private PNGChunk[] chunks;

    public PNGDane() {
        ileChunks = 0;
        chunks = new PNGChunk[10];
    }

    public void add(PNGChunk chunk) {
        chunks[ileChunks++] = chunk;
        if (ileChunks >= chunks.length) {
            PNGChunk[] largerArray = new PNGChunk[chunks.length + 10];
            System.arraycopy(chunks, 0, largerArray, 0, chunks.length);
            chunks = largerArray;
        }
    }

// =============================
    /*
     * IHDR must be the first chunk; it contains the header.
     * Funkcje do wyciągania informacji o PNG zachowanych w chunks o typie IHDR
   Width:              4 bytes
   Height:             4 bytes
   Bit depth:          1 byte
   Color type:         1 byte
   Compression method: 1 byte
   Filter method:      1 byte
   Interlace method:   1 byte

     */


    public long getWidth() {
        return getChunk("IHDR").getUnsignedInt(0);
    }

    public long getHeight() {
        return getChunk("IHDR").getUnsignedInt(4);
    }

    public short getBitsPerPixel() {
        return getChunk("IHDR").getUnsignedByte(8);
    }

    public short getColorType() {
        return getChunk("IHDR").getUnsignedByte(9);
    }

    public short getCompression() {
        return getChunk("IHDR").getUnsignedByte(10);
    }

    public short getFilter() {
        return getChunk("IHDR").getUnsignedByte(11);
    }

    public short getInterlace() {
        return getChunk("IHDR").getUnsignedByte(12);
    }

    /**
     * Pobiera dane o kolorach z PLTE chunk.
     *
     * @return ColorModel
     */
    public ColorModel getColorModel() throws StegException {
        short colorType = getColorType();
        int bitsPerPixel = getBitsPerPixel();

        if (colorType == 3) {
            byte[] paletteData = getChunk("PLTE").getData();
            int paletteLength = paletteData.length / 3;
            return new IndexColorModel(bitsPerPixel, paletteLength,
                    paletteData, 0, false);
        }
        throw new StegException(new Exception("steg err"), StegException.BLAD_ZLY_FORMAT_PLIKU,"Plik nie jest skompresowany");
    }

    /**
     * Zamiana w Raster, ciąg bitów.
     * @return
     * @throws pliki.img.png.StegException
     */
    public WritableRaster getRaster() throws StegException {
        int width = (int) getWidth();
        int height = (int) getHeight();
        int bitsPerPixel = getBitsPerPixel();
        short colorType = getColorType();

        if (colorType == 3) {
            byte[] imageData = getPixele();
            DataBuffer db = new DataBufferByte(imageData, imageData.length);
            WritableRaster raster = Raster.createPackedRaster(db, width,
                    height, bitsPerPixel, null);
            return raster;
        } else {
            throw new StegException(new Exception("steg err"), StegException.BLAD_ZLY_FORMAT_PLIKU,"Plik nie jest skompresowany");
        }
    }

    public byte[] getPixele() throws StegException {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            // Zapis pixeli
            for (int i = 0; i < ileChunks; i++) {
                PNGChunk chunk = chunks[i];
                //IDATA to pixele chunk, wrzucam co tamjest i będę miał pixele
                if (chunk.getTypString().equals("IDAT")) {
                    out.write(chunk.getData());
                }
            }
            out.flush();

            // Sprytna klasa od javy do deflacji tego co było w IDAT
            InflaterInputStream in = new InflaterInputStream(
                    new ByteArrayInputStream(out.toByteArray()));
            ByteArrayOutputStream inflatedOut = new ByteArrayOutputStream();
            int readLength;
            byte[] block = new byte[8192];
            while ((readLength = in.read(block)) != -1) {
                inflatedOut.write(block, 0, readLength);
            }
            inflatedOut.flush();
            byte[] imageData = inflatedOut.toByteArray();
            // sprawdzić długość
            int width = (int) getWidth();
            int height = (int) getHeight();
            int bitsPerPixel = getBitsPerPixel();
            int length = width * height * bitsPerPixel / 8;

            byte[] prunedData = new byte[length];

            // Jeśli jest użyte interlaced to niech spada
            if (getInterlace() == 0) {
                int index = 0;
                for (int i = 0; i < length; i++) {
                    if ((i * 8 / bitsPerPixel) % width == 0) {
                        index++; // Pomijamy bajt filtru
                    }
                    prunedData[i] = imageData[index++];
                }
            } else {
                throw new StegException(new Exception("steg err"), StegException.BLAD_ZLY_FORMAT_PLIKU,"Plik zawiera technikę interlaced.");
            }

            return prunedData;
        } catch (IOException ioe) {
            throw new StegException(ioe, StegException.BLAD_ZLY_FORMAT_PLIKU,"Błąd deflacji.");
        }
    }

    public PNGChunk getChunk(String type) {
        for (int i = 0; i < ileChunks; i++) {
            if (chunks[i].getTypString().equals(type)) {
                return chunks[i];
            }
        }
        return null;
    }

    public int getChunksNumber() {

        return chunks.length;
    }
}
